#include "StdAfx.h"
#include "GoBoardWithCommonProcess.h"

#include "string"

const int8_t NONE_LOCATION = -1;


CGoBoardWithCommonProcess::CGoBoardWithCommonProcess(int8_t goBoardLineCount): m_GoBoard(goBoardLineCount)
{
	m_JieLocaton.x = NONE_LOCATION;
	m_JieLocaton.y = NONE_LOCATION;

	m_LastMoveLocation.x = NONE_LOCATION;
	m_LastMoveLocation.y = NONE_LOCATION;

	m_PlayerToMove = BLACK_COLOR;

	m_PiecesNumber[BLACK_COLOR] = 0;
	m_PiecesNumber[WHITE_COLOR] = 0;

	m_HasJustPassed[BLACK_COLOR] = false;
	m_HasJustPassed[WHITE_COLOR] = false;
}


void CGoBoardWithCommonProcess::Init(void)
{
	m_GoBoard.Init();
}


void CGoBoardWithCommonProcess::Copy(const CGoBoardWithCommonProcess &src)
{
	m_GoBoard.Copy(src.m_GoBoard);
	m_JieLocaton = src.m_JieLocaton;
	m_LastMoveLocation = src.m_LastMoveLocation;
	m_PlayerToMove = src.m_PlayerToMove;
	m_PiecesNumber[BLACK_COLOR] = src.m_PiecesNumber[BLACK_COLOR];
	m_PiecesNumber[WHITE_COLOR] = src.m_PiecesNumber[WHITE_COLOR];
	m_HasJustPassed[BLACK_COLOR] = src.m_HasJustPassed[BLACK_COLOR];
	m_HasJustPassed[WHITE_COLOR] = src.m_HasJustPassed[WHITE_COLOR];
}


CGoBoardWithCommonProcess::~CGoBoardWithCommonProcess(void)
{
}


void CGoBoardWithCommonProcess::Clear(void)
{
	m_JieLocaton.x = NONE_LOCATION;
	m_JieLocaton.y = NONE_LOCATION;

	m_LastMoveLocation.x = NONE_LOCATION;
	m_LastMoveLocation.y = NONE_LOCATION;

	m_PlayerToMove = BLACK_COLOR;

	m_PiecesNumber[BLACK_COLOR] = 0;
	m_PiecesNumber[WHITE_COLOR] = 0;

	m_HasJustPassed[BLACK_COLOR] = false;
	m_HasJustPassed[WHITE_COLOR] = false;

	m_GoBoard.Clear();
}


void CGoBoardWithCommonProcess::SetRangzi(const SLocationOnGoBoard &point)
{
	ASSERT(m_LastMoveLocation.x == NONE_LOCATION && m_LastMoveLocation.y == NONE_LOCATION);
	m_GoBoard.SetPointStatus(BLACK_COLOR, point, PIECE_ON_POINT);
}


bool CGoBoardWithCommonProcess::Move(const SLocationOnGoBoard &pointToMove)
{
	ASSERT(m_GoBoard.GetPointStatus(BLACK_COLOR, pointToMove) == EMPTY_ON_POINT &&
		m_GoBoard.GetPointStatus(WHITE_COLOR, pointToMove) == EMPTY_ON_POINT);

	if (pointToMove.x == m_JieLocaton.x && pointToMove.y == m_JieLocaton.y) return false;

	m_JieLocaton.x = NONE_LOCATION;
	m_JieLocaton.y = NONE_LOCATION;

	m_GoBoard.SetPointStatus(m_PlayerToMove, pointToMove, PIECE_ON_POINT);

	int hasAteOppossiteQikuaiCount = 0;
	int hasAteOppossitePiecesCount = 0;
	int8_t scriptAdd[4][2] = {{-1, 0}, {0, 1}, {1, 0}, {0, -1}};
	for (int i=0; i<4; i++) {
		SLocationOnGoBoard sidePoint;
		sidePoint.x = pointToMove.x + scriptAdd[i][0];
		sidePoint.y = pointToMove.y + scriptAdd[i][1];

		if (!LocationIsValid(sidePoint)) continue;

		if (m_GoBoard.GetPointStatus(~m_PlayerToMove & 1, sidePoint) == EMPTY_ON_POINT) continue;

		CQichuan oppossiteQichuan(~m_PlayerToMove & 1);
		GetQichuanWithPoint(sidePoint, &oppossiteQichuan);
		int airCount = GetAirCountOfQichuan(oppossiteQichuan);
		if (airCount == 0) {
			EatQichuan(oppossiteQichuan);
			m_PiecesNumber[~m_PlayerToMove & 1] -= oppossiteQichuan.m_PieceLocationVector.size();
			hasAteOppossiteQikuaiCount++;
			hasAteOppossitePiecesCount = oppossiteQichuan.m_PieceLocationVector.size();
		}
	}

	 if (hasAteOppossiteQikuaiCount == 0) {
		CQichuan selfQichuan(m_PlayerToMove);
		GetQichuanWithPoint(pointToMove, &selfQichuan);
		int airCount = GetAirCountOfQichuan(selfQichuan);

		if (airCount == 0) {
			m_GoBoard.SetPointStatus(m_PlayerToMove, pointToMove, EMPTY_ON_POINT);
			return false;
		}
	 } else if (hasAteOppossiteQikuaiCount == 1 && hasAteOppossitePiecesCount == 1) {
		 SetJieLocation(pointToMove, m_PlayerToMove);
	 }

	m_LastMoveLocation = pointToMove;
	m_PiecesNumber[m_PlayerToMove]++;
	m_HasJustPassed[m_PlayerToMove] = false;
	m_PlayerToMove = GetOppossitePlayer();
	return true;
}


void CGoBoardWithCommonProcess::Shuzi(const CGoBoard &livingPiecesBoard,
	TLocationVector *pBlackArea,
	TLocationVector *pWhiteArea,
	SGameResult *pConsequence) const
{
	ShuziForOneColor(livingPiecesBoard, BLACK_COLOR, pWhiteArea);
	ShuziForOneColor(livingPiecesBoard, WHITE_COLOR, pBlackArea);
	pConsequence->blackScore = pBlackArea->size() + m_PiecesNumber[BLACK_COLOR];
	pConsequence->whiteScore = pWhiteArea->size() + m_PiecesNumber[WHITE_COLOR];
	TRACE("pieceNumber,b:%d, w:%d\n ", m_PiecesNumber[BLACK_COLOR], m_PiecesNumber[WHITE_COLOR]);
	TRACE("area, b:%d, w:%d\n", pBlackArea->size(), pWhiteArea->size());
}


void CGoBoardWithCommonProcess::ChangeDeadQichuan(CGoBoard *pLivingPiecesBoard,
	const SLocationOnGoBoard &point)
{
	TGoBoardPointStatus blackBoardPoint = m_GoBoard.GetPointStatus(BLACK_COLOR, point);
	TGoBoardPointStatus whiteBoardPoint = m_GoBoard.GetPointStatus(WHITE_COLOR, point);
	
	ASSERT(!(blackBoardPoint == PIECE_ON_POINT && whiteBoardPoint == PIECE_ON_POINT));

	TPlayerColor color;
	if (blackBoardPoint == PIECE_ON_POINT) {
		color = BLACK_COLOR;
	} else if (whiteBoardPoint == PIECE_ON_POINT) {
		color = WHITE_COLOR;
	} else {
		return;
	}

	CQichuan qichuan(color);
	TGoBoardPointStatus status = !pLivingPiecesBoard->GetPointStatus(color, point);
	GetQichuanWithPoint(point, &qichuan);
	for (size_t i=0; i<qichuan.m_PieceLocationVector.size(); i++) {
		pLivingPiecesBoard->SetPointStatus(color,
			qichuan.m_PieceLocationVector[i], status);
	}
}


void CGoBoardWithCommonProcess::Pass(void)
{
	m_JieLocaton.x = NONE_LOCATION;
	m_JieLocaton.y = NONE_LOCATION;

	m_LastMoveLocation.x = NONE_LOCATION;
	m_LastMoveLocation.y = NONE_LOCATION;

	m_HasJustPassed[m_PlayerToMove] = true;

	m_PlayerToMove = ~m_PlayerToMove & 1;
}


bool CGoBoardWithCommonProcess::IsGameEnd(void) const
{
	return m_HasJustPassed[BLACK_COLOR] && m_HasJustPassed[WHITE_COLOR];
}


void CGoBoardWithCommonProcess::GetQichuanWithPoint(const SLocationOnGoBoard &point,
	CQichuan *pQichuan)
{
	ASSERT(pQichuan != NULL);
	ASSERT(m_GoBoard.GetPointStatus(pQichuan->m_Color,point) == PIECE_ON_POINT);
	ASSERT(LocationIsValid(point));

	TOpenTable openTable;
	TCloseTable closeTable(CompForSLocationOnBoard);
	openTable.push(point);
	while (!openTable.empty()) {
		ExpandStackTopNodeInDFS(&openTable, &closeTable, pQichuan->m_Color);
	}
	CopyFromCloseTableToQichuan(closeTable,pQichuan);
}


int CGoBoardWithCommonProcess::GetAirCountOfQichuan(CQichuan &qichuan) const
{
	int airCount = 0;
	TCloseTable closeTable(CompForSLocationOnBoard);
	int scriptAdd[4][2] = {{-1, 0}, {0, 1}, {1, 0}, {0, -1}};
	for (TLocationVector::iterator item = qichuan.m_PieceLocationVector.begin();
		item!=qichuan.m_PieceLocationVector.end(); item++) {
		for (int i=0; i<4; i++) {
			SLocationOnGoBoard location;
			location.x = item->x + scriptAdd[i][0];
			location.y = item->y + scriptAdd[i][1];

			if (!LocationIsValid(location)) continue;

			if (m_GoBoard.GetPointStatus(BLACK_COLOR, location) == EMPTY_ON_POINT &&
				m_GoBoard.GetPointStatus(WHITE_COLOR, location) == EMPTY_ON_POINT) {
				if (closeTable.find(location) == closeTable.end()) {
					airCount++;
					closeTable.insert(location);
				}
			}
		}
	}
	return airCount;
}


void CGoBoardWithCommonProcess::EatQichuan(CQichuan &qichuan)
{
	for (TLocationVector::iterator item=qichuan.m_PieceLocationVector.begin();
		item!=qichuan.m_PieceLocationVector.end(); ++item) {
			m_GoBoard.SetPointStatus(qichuan.m_Color, *item, EMPTY_ON_POINT);
	}
}


bool CGoBoardWithCommonProcess::LocationIsValid(const SLocationOnGoBoard &location) const
{
	if (location.x >= 0 && location.x < m_GoBoard.m_LineCount && location.y >= 0 &&
		location.y < m_GoBoard.m_LineCount) {
		return true;
	} else {
		return false;
	}
}


void CGoBoardWithCommonProcess::ShuziForOneColor(const CGoBoard &livingPiecesBoard, TPlayerColor color,
	TLocationVector *pArea) const
{
	CGoBoardWithCommonProcess goBoard(m_GoBoard.m_LineCount);
	goBoard.m_GoBoard.Copy(livingPiecesBoard);
	goBoard.SetOneBoardToOppossiteStatus(color);
	int square = m_GoBoard.m_LineCount * m_GoBoard.m_LineCount;
	bool *flagArray = new bool[square];
	memset(flagArray, false, square);

	for (int i=0; i<square; i++) {
		if (goBoard.m_GoBoard.m_OneColorBoard[color][i] == PIECE_ON_POINT && !flagArray[i]) {
			CQichuan qichuan(color);
			SLocationOnGoBoard point = {i % m_GoBoard.m_LineCount, i / m_GoBoard.m_LineCount};
			goBoard.GetQichuanWithPoint(point, &qichuan);
			for (size_t j=0; j<qichuan.m_PieceLocationVector.size(); j++) {
				int script = qichuan.m_PieceLocationVector[j].x +
					goBoard.m_GoBoard.m_LineCount * qichuan.m_PieceLocationVector[j].y;
				flagArray[script] = true;
			}
			int airCount = goBoard.GetAirCountOfQichuan(qichuan);
			if (airCount == 0) {
				for (size_t j=0; j<qichuan.m_PieceLocationVector.size(); j++) {
					pArea->push_back(qichuan.m_PieceLocationVector[j]);
				}
			}
		}
	}

	delete[] flagArray;
}


void CGoBoardWithCommonProcess::SetJieLocation(const SLocationOnGoBoard &movePoint,
	TPlayerColor moveColor)
{
	int oppossiteColorPiecesCount = 0;
	SLocationOnGoBoard emptyLocation;
	int scriptAdd[4][2] = {{1, 0}, {0, -1}, {-1, 0}, {0, 1}};

	for (int i=0; i<4; i++) {
		SLocationOnGoBoard sidePoint;
		sidePoint.x = movePoint.x + scriptAdd[i][0];
		sidePoint.y = movePoint.y + scriptAdd[i][1];

		if (!LocationIsValid(sidePoint)){
			oppossiteColorPiecesCount++;
			continue;
		}

		TGoBoardPointStatus moveColorStatus = m_GoBoard.GetPointStatus(moveColor,sidePoint);
		TGoBoardPointStatus oppositeColorStatus = m_GoBoard.GetPointStatus(~moveColor & 1,sidePoint);
		if (oppositeColorStatus == PIECE_ON_POINT) {
			oppossiteColorPiecesCount++;
		} else if (oppositeColorStatus == EMPTY_ON_POINT && moveColorStatus == EMPTY_ON_POINT) {
			emptyLocation = sidePoint;
		}
	}

	if (oppossiteColorPiecesCount == 3) {
		m_JieLocaton = emptyLocation;
		TRACE("jie: (%d, %d)\n", m_JieLocaton.x, m_JieLocaton.y);
	}
}


bool CGoBoardWithCommonProcess::CompForSLocationOnBoard(const SLocationOnGoBoard &lowLocation,
	const SLocationOnGoBoard &highLocation)
{
	if (lowLocation.y * 19 + lowLocation.x < highLocation.y * 19 +highLocation.x) {
		return true;
	} else {
		return false;
	}
}


void CGoBoardWithCommonProcess::ExpandStackTopNodeInDFS(TOpenTable *pOpenTable, TCloseTable *pCloseTable,
	TPlayerColor pieceColor) const
{
	int scriptMove[4][2] = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
	const SLocationOnGoBoard topNode = pOpenTable->top();

	pOpenTable->pop();
	for (int j=0; j<4; j++) {
		int scriptX = topNode.x + scriptMove[j][0];
		int scriptY = topNode.y + scriptMove[j][1];
		SLocationOnGoBoard nextNodeOnBoardLocation = {scriptX, scriptY};
		if (LocationIsValid(nextNodeOnBoardLocation)) {
			ProcessCurrentNodeOnBoardInDFS(pOpenTable, *pCloseTable, nextNodeOnBoardLocation, pieceColor);
		}
	}

	pCloseTable->insert(topNode);
}


void CGoBoardWithCommonProcess::ProcessCurrentNodeOnBoardInDFS(TOpenTable *pOpenTable,
	const TCloseTable &closeBoard,
	const SLocationOnGoBoard &currentLocationOnBoard,
	TPlayerColor color) const
{
	if (m_GoBoard.GetPointStatus(color,currentLocationOnBoard) == PIECE_ON_POINT == PIECE_ON_POINT) {
		if (closeBoard.find(currentLocationOnBoard) == closeBoard.end()) {
			pOpenTable->push(currentLocationOnBoard);
		}
	}
}


void CGoBoardWithCommonProcess::CopyFromCloseTableToQichuan(const TCloseTable &closeTable,
	CQichuan *pQichuan) const
{
	TCloseTable::iterator item;
	for (item=closeTable.begin(); item!=closeTable.end(); ++item) {
		pQichuan->m_PieceLocationVector.push_back(*item);
	}
}


void CGoBoardWithCommonProcess::SetOneBoardToOppossiteStatus(TPlayerColor color)
{
	int square = m_GoBoard.m_LineCount * m_GoBoard.m_LineCount;
	for (int i=0; i<square; i++) {
		(m_GoBoard.m_OneColorBoard[color])[i] = !(m_GoBoard.m_OneColorBoard[BLACK_COLOR])[i] &&
			!(m_GoBoard.m_OneColorBoard[WHITE_COLOR])[i];
	}
}